package ru.compft.web.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import ru.compft.common.data.Module;
import ru.compft.common.data.ModulePage;
import ru.compft.common.services.ModuleService;
import ru.compft.common.utils.GlobalConstants;
import ru.compft.web.WebConst;
import ru.compft.web.utils.FacesUtils;
import ru.compft.web.utils.ViewUtils;
import ru.compft.web.view.CurrentView;

import javax.faces.application.NavigationHandler;
import javax.faces.application.ViewHandler;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIViewRoot;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.ComponentSystemEvent;
import javax.faces.event.PhaseId;
import java.io.IOException;
import java.io.Serializable;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;


@ManagedBean(name = WebConst.NAVIGATION_CONTROLLER)
@ApplicationScoped
public class NavigationController implements Serializable {
    private static final long serialVersionUID = -191609381331714887L;

    private static final Logger logger = LoggerFactory.getLogger(NavigationController.class);

    public void loadModule() {
        final PhaseId phaseId = FacesUtils.getCurrentPhaseId();
        System.out.println("loadModule = " + phaseId);

        final Map<String, String> map = FacesUtils.getExternalContext().getRequestParameterMap();
        String module = map.get(CurrentView.MODULE_PARAM);

        System.out.println("module = " + module);
        if(module == null) module = "";
        FacesContext context = FacesContext.getCurrentInstance();
        ViewHandler handler = context.getApplication().getViewHandler();
        String viewId = "/modules"+module+"/index.xhtml";
        UIViewRoot root = handler.createView(context, viewId);

        root.setViewId(viewId);
        context.setViewRoot(root);

//        loadPage("/modules/setting/setting2.xhtml");
    }
//    public void loadModule(ComponentSystemEvent event) {
    public void loadModule2() {
        final PhaseId phaseId = FacesUtils.getCurrentPhaseId();
        System.out.println("loadModule2  = " + phaseId);
        final Map<String, String> map = FacesUtils.getExternalContext().getRequestParameterMap();
        final String tab = map.get(CurrentView.TAB_PARAM);

        final CurrentView currentView = (CurrentView) FacesUtils.getBackingBean(WebConst.CURRENT_VIEW);
        if (currentView != null) {

            if (StringUtils.hasText(tab)) {
                // если есть tab, установим его в текущее отображение
//                currentView.setTab(tab);
//            navigate(event);
            } else {
                // первая загрузка модуля
                currentView.setTab(null);
                System.out.println("loadModule 2= ");
//        final ServletRequest servletRequest = (ServletRequest) FacesUtils.getExternalContext().getRequest();
//        final String tab = servletRequest.getParameter("tab");


                // если страница не задана, очистим ее
//            if (!StringUtils.hasText(tab))
//                currentView.setCurrentModulePage(null);

                final UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
                final String viewId = viewRoot.getViewId();
                final String code = ViewUtils.getModuleCodeByViewId(viewId);

                // Если модуль не поменялся, то нет смысла его перезагружать.
                // просто найдем его страницу и установим как текущую
                if (StringUtils.hasText(code)) {
//                if (currentModule == null || !code.equals(currentModule.getCode())) {
                    // обнулим предыдущий модуль и страницу если они были установлены
//                    clearAll();
                    final Module currentModule = loadModuleByCode(code);
//                }
//            } else {
                    // если в запросе нет модуля, очищаем все
//                clearAll();
//            }

                    if (currentModule != null) {
                        currentView.setCurrentModule(currentModule);

                        final Map<String, ModulePage> pageMap = new HashMap<String, ModulePage>();

                        currentView.setPageList(currentModule.getPageList());
//                    boolean founded = false; // признак того, что страницу нашли
//                    if (!CollectionUtils.isEmpty(modulePageList)) {
//                        for (ModulePage modulePage : modulePageList) {
//                            final String modulePageCode = modulePage.getCode();
//                            pageMap.put(modulePageCode, modulePage);

//                            if (!getPageCodeList().contains(modulePageCode)) {
//                                getPageCodeList().add(modulePageCode);
//                            }

//                            if (modulePageCode.equals(tab)) {
//                                setCurrentModulePage(modulePage);
//                                founded = true;
//                            }
//                        }
//
//                        currentView.setPageMap(pageMap);
//                    }

//                    if (StringUtils.hasText(tab) && !founded) {
//                        pageNotFound(tab);
//                    }
                    } else {
                        moduleNotFound(code);
                    }

//        System.out.println("MODULE = " + code);
//        System.out.println("PAGE = " + tab);
                }
            }
        }
    }

    /**
     * Загружаем модуль в качестве текущего по его пути
     *
     * @param code
     */
    private Module loadModuleByCode(String code) {
        if (logger.isDebugEnabled()) {
            logger.debug("Try load module by code = " + code);
        }

        if (StringUtils.hasText(code)) {
            final ModuleService moduleService = (ModuleService) FacesUtils.getSpringBean(WebConst.MODULE_SERVICE);
            final Module module = moduleService.getModuleByCode(code);

            // устанавливаем путь к текущей странице
            // todo сделать ModulePermitter - проверку на доступность данного модуля для пользователя
            // todo иначе возможен прямой переход - "хакерская" атака
//            if (module != null) {
//                setCurrentModule(module);

            if (logger.isDebugEnabled()) {
                logger.debug("Module {" + code + "} found and set as current");
            }
            return module;
//            }
        }
        return null;
    }

    /**
     * Пишем сообщение, что страница не найдена
     *
     * @param tab
     */
    private void pageNotFound(String tab) {
        // Страница не найдена
        // todo сделать через файл messages
        FacesUtils.addErrorMessage(null, "Страница не найдена");
        if (logger.isDebugEnabled()) {
            logger.debug("ModulePage with code {" + tab + "} not found");
        }
    }

    /**
     * Пишем сообщение, что модуль не найден
     *
     * @param code
     */
    private void moduleNotFound(String code) {
        // Модуль не найден
        // todo сделать через файл messages
        FacesUtils.addErrorMessage(null, "Модуль не найден");
        if (logger.isDebugEnabled()) {
            logger.debug("Module with code {" + code + "} not found");
        }
    }

    /**
     * Approach to navigation that will grab our params via the request map
     * This is meant to be used with a full page refresh as metadata (called on each page refresh as postValidate listener)
     */
    public void navigate(ComponentSystemEvent event) {
        System.out.println(" !!!!!!! navigate ---------- " + event);
        Map<String, String> map = FacesUtils.getExternalContext().getRequestParameterMap();
        System.out.println(" !!!!!!! map ---------- " + map);
        navigate(map.get(CurrentView.TAB_PARAM), map.get(CurrentView.PAGE_PARAM));
    }

    /**
     * Approach to navigation that will try to grab f:attributes that were specified on a command component
     * This is meant to be used with a commandLink
     */
    public void navigate(ActionEvent event) {
        Object newTab = FacesUtils.getFAttribute(event, CurrentView.TAB_PARAM);
        Object newPage = FacesUtils.getFAttribute(event, CurrentView.PAGE_PARAM);

        System.out.println(" !!!!!!! navigate newTab=" + newTab + " newPage=" + newPage);

        navigate(newTab == null ? null : newTab.toString(),
                newPage == null ? null : newPage.toString());
    }

//    public void onMenuPaneChange(AccordionPaneChangeEvent event)
//    {
//        //get parameters
//        String tabParamValue = event.getTab().getAttributes().get(CurrentView.TAB_PARAM).toString();
//        String pageParamValue = (String)event.getTab().getAttributes().get(CurrentView.PAGE_PARAM).toString();
//        //set CurrentView variables via main controller logic method navigate(String String)
//        navigate(tabParamValue == null ? null : tabParamValue, pageParamValue == null ? null : pageParamValue);
//        //prepare redirect URL
//        ExternalContext extContext = FacesContext.getCurrentInstance().getExternalContext();
//        String redirectURL = prepareRedirectURL(tabParamValue, pageParamValue, extContext);
//        //issue redirect request
//        try {
//            extContext.redirect(redirectURL);
//        } 
//        catch (IOException e) {
//            extContext.log(getClass().getName() + ".invokeRedirect", e);
//        }
//    }

    /**
     * Main controller logic.
     */
    public void navigate(String newTab, String newPage) {
        //CurrentViewBean constructor is called here
        CurrentView currentView = (CurrentView) FacesUtils.getBackingBean(WebConst.CURRENT_VIEW);

        // If our new tab is valid then set it into the currentView
        if (newTab != null) {
            currentView.setTab(newTab);
        }

        // If our new page is valid then set it into the currentView
        if (newPage != null) {
            currentView.setPage(newPage);
        } else {
            // Reset the page if the tab changed and the page was null
            // This happens if the overall tab menu was selected
            // Doing this will ensure that the default page for the current tab is set
            if (newTab != null) {
                currentView.setPage(newPage);
            }
        }

        loadDefaultNavigation(currentView);

        // Update our tab and page to the latest
//        newTab = currentView.getTab();
//        newPage = currentView.getPage();

        // Check whether we need to load the default navigation (this portion is executed during application startup)
//        if (newTab == null && newPage == null) {
//            loadDefaultNavigation(currentView);
//        }
        // Otherwise we may just need to load the page (this portion is executed during switch between tabs)
//        else if (newTab != null && newPage == null) {
//            Menu menu = (Menu)FacesUtils.getBackingBean(newTab);
//            if (menu != null) {
//                currentView.setCurrentComponentGroup(menu);
//                ComponentExampleImpl compExample = (ComponentExampleImpl)
//                        FacesUtils.getBackingBean(menu.getDefaultExample().getExampleBeanName());
//                if (compExample != null) {
//                    currentView.setCurrentComponentExample(compExample);
//                }
//                renderUIbasedOnExample(currentView.getCurrentComponentGroup().getDefaultExample().getExampleBeanName(), currentView);
//            } else {
//                loadDefaultNavigation(currentView);
//            }
        // Otherwise check if the tab/page is valid at all (this portion is executed during switch between pages within tab)
//        } else {
//            Object tmpGroup = FacesUtils.getBackingBean(newTab);
//            Object tmpExample = FacesUtils.getBackingBean(newPage);
//            if ((tmpGroup != null && tmpGroup instanceof Menu) && (tmpExample != null && tmpExample instanceof ComponentExampleImpl)) 
//            {
//                ComponentExampleImpl currentExample = currentView.getCurrentComponentExample();
//                ComponentExampleImpl setExample = (ComponentExampleImpl)tmpExample;

        //ENABLE/DISABLE CODE BELOW IF SWITCHING TO/FROM ACCORDION MENU TO THE REGULAR MENU(based on links) IN showcase.xhtml
        // Determine if we need to fire the effect
        // This is necessary when the overall demo was changed, so basically when the package is different
        // Although checking against packages is not the desired approach, there isn't another option for matching beans
        //  because they have no built-in idea of a parent-child heirarchy
//                if (currentExample != null) {
//                    if (!setExample.getClass().getPackage().equals(currentExample.getClass().getPackage())) {
//                        setExample.prepareEffect();
//                    }
//                }
//                else {
//                    setExample.prepareEffect();
//                }

        // Apply the new tab and page
//                currentView.setCurrentComponentGroup((Menu)tmpGroup);
//                currentView.setCurrentComponentExample(setExample);
        //determine how shuite menu and page content should appear in UI
//            renderUIbasedOnExample(newPage, currentView);
//        }
//        else {
//                loadDefaultNavigation(currentView);
//            }
//        }
    }

    //The method below is used with accordion main menu - DO NOT REMOVE
    //If page = overview showcase then set active accordion tab tab to 0 and only render description portion of the showcase
    //If page = regular showcase, set active acordion tab based on redirect request parameters and display full showcase (with description, page content and source code)
    private void renderUIbasedOnExample(String pageDescription, CurrentView currentView) {
        //determine if page source code should appear in UI:
//        currentView.setRenderAsExample(isContentAnExample(pageDescription));
//ENABLE/DISABLE CODE BELOW IF SWITCHING TO/FROM ACCORDION MENU TO THE REGULAR MENU(based on links) IN showcase.xhtml
//        if(currentView.isRenderAsExample()) {
//            //set accordion menu tab
//            currentView.setActivePaneIndex(findActivePaneIndex(pageDescription, currentView));
//        }
//        else {
//            //set accordion menu tab to the first one:
//            currentView.setActivePaneIndex(CurrentView.DEFAULT_MENU_TAB_INDEX);
//        }
    }

    private void loadDefaultNavigation(CurrentView currentView) {
        // load default pages.
        final StringBuilder viewId = new StringBuilder(GlobalConstants.MODULES);
        final Module currentModule = currentView.getCurrentModule();
        if (currentModule != null) {
            viewId.append(GlobalConstants.SLASH).append(currentModule.getCode());
        }
//        viewId.append(GlobalConstants.INDEX).append(GlobalConstants.XHTML_FILE_SUFFIX);
        viewId.append("/systemPropsEdit").append(GlobalConstants.XHTML_FILE_SUFFIX);

        reloadPage(currentView, viewId.toString());
//        currentView.setCurrentComponentGroup((Menu)
//                FacesUtils.getBackingBean(CurrentView.DEFAULT_MENU));
//        currentView.setTab(CurrentView.DEFAULT_MENU);
//        
//        String beanName = currentView.getCurrentComponentGroup().getDefaultExample().getExampleBeanName();
//        currentView.setCurrentComponentExample((ComponentExampleImpl)FacesUtils.getBackingBean(beanName));
//        currentView.setPage(beanName);
//        currentView.getCurrentComponentGroup().getBeanName();
//        renderUIbasedOnExample(beanName, currentView);
    }

    private String prepareRedirectURL(String tabParameterValue, String pageParameterValue, ExternalContext extContext) {
//        String viewId = CurrentView.DEFAULT_VIEW_ID;
        String viewId = GlobalConstants.MODULES;
        String charEncoding = extContext.getRequestCharacterEncoding();
        try {
            String tabParamName = URLEncoder.encode(CurrentView.TAB_PARAM, charEncoding);
            String tabParamValue = URLEncoder.encode(tabParameterValue, charEncoding);
            String pageParamName = URLEncoder.encode(CurrentView.PAGE_PARAM, charEncoding);
            String pageParamValue = URLEncoder.encode(pageParameterValue, charEncoding);

            viewId = extContext.getRequestContextPath() + viewId + '?' + tabParamName
                    + "=" + tabParamValue + "&" + pageParamName + "=" + pageParamValue;
            String urlLink = extContext.encodeActionURL(viewId);
            return urlLink;
        } catch (IOException e) {
            logger.error("During invokeRedirect", e);
            return null;
        }
    }

//    private int findActivePaneIndex(String pageDescription, CurrentView currentView) 
//    {
//         ArrayList<MenuLink> currentMenuStructure = currentView.getCurrentComponentGroup().getMenuLinks();
//         int tabIndex = 0;
//         for (MenuLink menuItem : currentMenuStructure) 
//         {
//            if(menuItem.getExampleBeanName().equals(pageDescription))
//            {
//                return tabIndex;
//            }
//            tabIndex++;
//         }
//         //if tabIndex was not returned by now, return current currentView.activePaneIndex (this happens when we switch from one page to another within same tab
//         if(tabIndex == currentMenuStructure.size())
//         {
//             tabIndex = currentView.getActivePaneIndex();
//         }
//         return tabIndex;
//    }

    public static void refreshPage() {
        FacesContext context = FacesContext.getCurrentInstance();
        ViewHandler handler = context.getApplication().getViewHandler();
        String viewId = context.getViewRoot().getViewId();
        UIViewRoot root = handler.createView(context, viewId);

        root.setViewId(viewId);
        context.setViewRoot(root);
    }

    public static void reloadPage(CurrentView currentView, String viewId) {
//        refreshPage();

        final StringBuilder newPage = new StringBuilder(viewId);
//        final StringBuilder newPage = new StringBuilder();

        if (StringUtils.hasText(currentView.getTab())) {
            newPage.append("?")
                    .append(CurrentView.TAB_PARAM)
                    .append("=")
                    .append(currentView.getTab());

            if (StringUtils.hasText(currentView.getPage())) {
                newPage.append("&")
                        .append(CurrentView.PAGE_PARAM)
                        .append("=")
                        .append(currentView.getPage());
            }
        }

        // Redirect to the current page with the current newPage
        loadPage(newPage.toString());
    }

    public static void loadPage(String page) {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        NavigationHandler myNav = facesContext.getApplication().getNavigationHandler();
        myNav.handleNavigation(facesContext, null, page);
//        try {
//            FacesUtils.getExternalContext().redirect(page);
//        } catch (Throwable e) {
//            logger.error(e.getMessage(), e);
//        }
    }

//    private boolean isContentAnExample(String pageDescription)
//    {
//        if(pageDescription.equals(AceSuiteOverviewBean.BEAN_NAME) || pageDescription.equals(IceSuiteOverviewBean.BEAN_NAME)) {
//            return false; 
//        }
//        else {
//            return true;
//        }
//    }
}